home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / stdwin / Packs / textedit / textlow.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-03  |  13.5 KB  |  716 lines  |  [TEXT/????]

  1. /* Text Edit, low-level routines */
  2.  
  3. /* XXX Should make return-less functions void */
  4.  
  5. /* CONVENTION:
  6.     routines beginning with te... have a first parameter 'tp';
  7.     routines beginning with z... don't, or are macros that
  8.     implicitly use a variable or parameter 'tp'
  9. */
  10.  
  11. #include "text.h"
  12.  
  13. extern void dprintf _ARGS((char *, ...));
  14.  
  15. /* Variants on wtextwidth, wtextbreak, wdrawtext taking the gap in account.
  16.    These have two buffer offsets as parameters instead of a text pointer
  17.    and a length; tetextbreak also returns a buffer offset */
  18.  
  19. /* These routines now also take tabs into account.
  20.    They should now only be called with a line start for 'pos' ! */
  21.  
  22. int
  23. tetextwidth(tp, pos, end)
  24.     register TEXTEDIT *tp;
  25.     bufpos pos, end;
  26. {
  27.     char *p= tp->buf + pos;
  28.     register char *e= tp->buf +
  29.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  30.     int w= 0;
  31.     register char *k;
  32.     
  33.     zcheckpos(pos);
  34.     zcheckpos(end);
  35.     zassert(pos <= end);
  36.     
  37.     /* This do loop is executed once or twice only! */
  38.     do {
  39.         for (k= p; k < e; ++k) {
  40.             if (*k == '\t') {
  41.                 if (k > p)
  42.                     w += wtextwidth(p, (int)(k-p));
  43.                 w= znexttab(w);
  44.                 p= k+1;
  45.             }
  46.         }
  47.         if (k > p)
  48.             w += wtextwidth(p, (int)(k-p));
  49.         if (tp->gap >= pos) {
  50.             p= tp->buf + zgapend;
  51.             e= tp->buf + end;
  52.         }
  53.     } while (k < e);
  54.     
  55.     return w;
  56. }
  57.  
  58. bufpos
  59. tetextbreak(tp, pos, end, width)
  60.     register TEXTEDIT *tp;
  61.     bufpos pos, end;
  62.     int width;
  63. {
  64.     char *p= tp->buf + pos;
  65.     register char *e= tp->buf +
  66.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  67.     int w= 0;
  68.     register char *k;
  69.     
  70.     zcheckpos(pos);
  71.     zcheckpos(end);
  72.     zassert(pos <= end);
  73.     
  74.     /* This do loop is executed once or twice only! */
  75.     do {
  76.         for (k= p; k < e; ++k) {
  77.             if (*k == '\t') {
  78.                 if (k > p) {
  79.                     int n= wtextbreak(p, (int)(k-p),
  80.                         width-w);
  81.                     if (n < k-p)
  82.                         return p - tp->buf + n;
  83.                     w += wtextwidth(p, (int)(k-p));
  84.                 }
  85.                 w= znexttab(w);
  86.                 if (w > width)
  87.                     return k - tp->buf;
  88.                 p= k+1;
  89.             }
  90.         }
  91.         if (k > p) {
  92.             int n= wtextbreak(p, (int)(k-p), width-w);
  93.             if (n < k-p)
  94.                 return p - tp->buf + n;
  95.             w += wtextwidth(p, (int)(k-p));
  96.         }
  97.         if (tp->gap >= pos) {
  98.             p= tp->buf + zgapend;
  99.             e= tp->buf + end;
  100.         }
  101.     } while (k < e);
  102.     
  103.     return end;
  104. }
  105.  
  106. hcoord
  107. tedrawtext(tp, h, v, pos, end)
  108.     register TEXTEDIT *tp;
  109.     int h, v;
  110.     bufpos pos, end;
  111. {
  112.     char *p= tp->buf + pos;
  113.     register char *e= tp->buf +
  114.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  115.     int w= 0;
  116.     register char *k;
  117.     
  118.     zcheckpos(pos);
  119.     zcheckpos(end);
  120.     zassert(pos <= end);
  121.     
  122.     /* This do loop is executed once or twice only! */
  123.     do {
  124.         for (k= p; k < e; ++k) {
  125.             if (*k == '\t') {
  126.                 if (k > p)
  127.                     wdrawtext(h+w, v, p, (int)(k-p));
  128.                     w += wtextwidth(p, (int)(k-p));
  129.                 w= znexttab(w);
  130.                 p= k+1;
  131.             }
  132.         }
  133.         if (k > p) {
  134.             wdrawtext(h+w, v, p, (int)(k-p));
  135.             w += wtextwidth(p, (int)(k-p));
  136.         }
  137.         if (tp->gap >= pos) {
  138.             p= tp->buf + zgapend;
  139.             e= tp->buf + end;
  140.         }
  141.     } while (k < e);
  142.     
  143.     return h+w;
  144. }
  145.  
  146. /* Safe memory allocation - these abort instead of returning NULL */
  147.  
  148. char *
  149. zmalloc(n)
  150.     int n;
  151. {
  152.     char *p= malloc((unsigned)n);
  153.     
  154.     if (p == NULL) {
  155.         dprintf("zmalloc(%d): out of mem", n);
  156.         exit(1);
  157.     }
  158.     return p;
  159. }
  160.  
  161. char *
  162. zrealloc(p, n)
  163.     char *p;
  164.     int n;
  165. {
  166.     char *q= realloc(p, (unsigned)n);
  167.     if (q == NULL) {
  168.         dprintf("zrealloc(0x%lx, %d): out of mem", (long)p, n);
  169.         exit(1);
  170.     }
  171.     return q;
  172. }
  173.  
  174. /* Create/destroy a text-edit record */
  175.  
  176. TEXTEDIT *
  177. tealloc(win, left, top, width)
  178.     WINDOW *win;
  179. {
  180.     return tesetup(win, left, top, left+width, top, TRUE);
  181. }
  182.  
  183. TEXTEDIT *
  184. tecreate(win, left, top, right, bottom)
  185.     WINDOW *win;
  186.     int left, top, right, bottom;
  187. {
  188.     return tesetup(win, left, top, right, bottom, TRUE);
  189. }
  190.  
  191. /*ARGSUSED*/
  192. TEXTEDIT *
  193. tesetup(win, left, top, right, bottom, drawing)
  194.     WINDOW *win;
  195.     int left, top, right, bottom;
  196.     bool drawing;
  197. {
  198.     TEXTEDIT *tp= (TEXTEDIT*) zmalloc(sizeof(TEXTEDIT));
  199.     TEXTATTR saveattr;
  200.     
  201.     tp->win= win;
  202.     tp->left= left;
  203.     tp->top= top;
  204.     tp->right= right;
  205.     tp->width= right-left;
  206.  
  207.     tp->viewing= FALSE;
  208.     
  209.     wgettextattr(&saveattr);
  210.     if (win != NULL) {
  211.         wgetwintextattr(win, &tp->attr);
  212.         wsettextattr(&tp->attr);
  213.     }
  214.     else
  215.         tp->attr = saveattr;
  216.     tp->vspace= wlineheight();
  217.     tp->tabsize= 8*wcharwidth(' ');
  218.     if (win != NULL)
  219.         wsettextattr(&saveattr);
  220.     
  221.     tp->bottom= tp->top + tp->vspace;
  222.     
  223.     tp->foc= tp->foclen= 0;
  224.     
  225.     tp->buflen= 1;
  226.     tp->buf= zmalloc(tp->buflen);
  227.     
  228.     tp->gap= 0;
  229.     tp->gaplen= tp->buflen;
  230.     
  231.     tp->nlines= 1;
  232.     tp->nstart= STARTINCR;
  233.     tp->start= (bufpos*) zmalloc(tp->nstart*sizeof(bufpos));
  234.     tp->start[0]= tp->start[1]= tp->buflen;
  235.     
  236.     tp->aim= UNDEF;
  237.     tp->focprev= FALSE;
  238.     tp->hilite= FALSE;
  239.     tp->mdown= FALSE;
  240.     tp->drawing= tp->active= drawing;
  241.     tp->opt_valid= FALSE;
  242.     
  243.     if (tp->active)
  244.         tesetcaret(tp);
  245.     
  246.     zcheck();
  247.     
  248.     return tp;
  249. }
  250.  
  251.  
  252. void
  253. tedestroy(tp)
  254.     register TEXTEDIT *tp;
  255. {
  256.     if (tp->viewing)
  257.         wchange(tp->win, tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  258.     else
  259.         wchange(tp->win, tp->left, tp->top, tp->right, tp->bottom);
  260.     tefree(tp);
  261. }
  262.  
  263. void
  264. tefree(tp)
  265.     register TEXTEDIT *tp;
  266. {
  267.     if (tp->active) {
  268.         wnocaret(tp->win);
  269.         tehidefocus(tp);
  270.     }
  271.     if (tp->buf != NULL)
  272.         free(tp->buf);
  273.     if (tp->start != NULL)
  274.         free((char*)tp->start);
  275.     free((char*)tp);
  276. }
  277.  
  278. void
  279. tesetactive(tp, active)
  280.     register TEXTEDIT *tp;
  281.     bool active;
  282. {
  283.     if (!tp->drawing || tp->active == active)
  284.         return;
  285.     tp->active = active;
  286.     if (active) {
  287.         tesetcaret(tp);
  288.     }
  289.     else {
  290.         wnocaret(tp->win);
  291.         tehidefocus(tp);
  292.     }
  293. }
  294.  
  295. /* Show/hide the focus highlighting.
  296.    The boolean hilite is set when highlighting is visible.
  297.    teshowfocus ensures the highlighting is visible (if applicable);
  298.    tehidefocus ensures it is invisible.
  299.    teinvertfocus does the hard work (it is also called from zdraw) */
  300.  
  301. teshowfocus(tp)
  302.     register TEXTEDIT *tp;
  303. {
  304.     if (tp->active && !tp->hilite && tp->foclen > 0) {
  305.         wbegindrawing(tp->win);
  306.         if (tp->viewing)
  307.             wcliprect(tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  308.         teinvertfocus(tp);
  309.         wenddrawing(tp->win);
  310.         tp->hilite= TRUE;
  311.     }
  312. }
  313.  
  314. tehidefocus(tp)
  315.     register TEXTEDIT *tp;
  316. {
  317.     if (tp->hilite) {
  318.         wbegindrawing(tp->win);
  319.         if (tp->viewing)
  320.             wcliprect(tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  321.         teinvertfocus(tp);
  322.         wenddrawing(tp->win);
  323.         tp->hilite= FALSE;
  324.     }
  325. }
  326.  
  327. static
  328. teinvertfocus(tp)
  329.     register TEXTEDIT *tp;
  330. {
  331.     teinvert(tp, tp->foc, zfocend);
  332. }
  333.  
  334. /* Change to a new focus.
  335.    Sometimes this may keep the focus visible, sometimes not. */
  336.  
  337. techangefocus(tp, f1, f2)
  338.     register TEXTEDIT *tp;
  339.     int f1, f2;
  340. {
  341.     if (tp->hilite) {
  342.         wbegindrawing(tp->win);
  343.         if (tp->viewing)
  344.             wcliprect(tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  345.         if (f1 == tp->foc)
  346.             teinvert(tp, zfocend, f2);
  347.         else if (f2 == zfocend)
  348.             teinvert(tp, f1, tp->foc);
  349.         else {
  350.             teinvert(tp, tp->foc, zfocend);
  351.             tp->hilite= FALSE;
  352.         }
  353.         wenddrawing(tp->win);
  354.     }
  355.     tp->foc= f1;
  356.     tp->foclen= f2-f1;
  357. }
  358.  
  359. /* Low-level interface: invert the area between f1 and f2 */
  360.  
  361. static
  362. teinvert(tp, f1, f2)
  363.     register TEXTEDIT *tp;
  364.     int f1, f2;
  365. {
  366.     coord h, v, hlast, vlast;
  367.     
  368.     if (f1 == f2)
  369.         return;
  370.     if (f2 < f1) {
  371.         int temp= f1;
  372.         f1= f2;
  373.         f2= temp;
  374.     }
  375.     
  376.     tewhichpoint(tp, f1, &h, &v);
  377.     tewhichpoint(tp, f2, &hlast, &vlast);
  378.     
  379.     if (v == vlast)
  380.         winvert(h, v, hlast, v + tp->vspace);
  381.     else {
  382.         winvert(h, v, tp->right, v + tp->vspace);
  383.         winvert(tp->left, v + tp->vspace, tp->right, vlast);
  384.         winvert(tp->left, vlast, hlast, vlast + tp->vspace);
  385.     }
  386. }
  387.  
  388. /* Draw procedure */
  389.  
  390. void
  391. tedraw(tp)
  392.     register TEXTEDIT *tp;
  393. {
  394.     tedrawnew(tp, tp->left, tp->top, tp->right, tp->bottom);
  395. }
  396.  
  397. void
  398. tedrawnew(tp, left, top, right, bottom)
  399.     register TEXTEDIT *tp;
  400.     coord left, top, right, bottom;
  401. {
  402.     lineno ifirst, ilast, i;
  403.  
  404.     /* Clip given area to view */
  405.     if (tp->viewing) {
  406.         CLIPMIN(left, tp->vleft);
  407.         CLIPMIN(top, tp->vtop);
  408.         CLIPMAX(right, tp->vright);
  409.         CLIPMAX(bottom, tp->vbottom);
  410.     }
  411.     
  412.     /* Restrict drawing to intersection of view and given area */
  413.     wcliprect(left, top, right, bottom);
  414.     
  415.     /* Compute first, last line to be drawn */
  416.     ifirst= (top - tp->top)/tp->vspace;
  417.     if (ifirst < 0)
  418.         ifirst= 0;
  419.     ilast= (bottom - tp->top + tp->vspace - 1)/tp->vspace;
  420.     if (ilast > tp->nlines)
  421.         ilast= tp->nlines;
  422.     
  423.     /* Draw lines ifirst...ilast-1 */
  424.     for (i= ifirst; i < ilast; ++i) {
  425.         bufpos pos= tp->start[i];
  426.         bufpos end= tp->start[i+1];
  427.         if (end > pos && zcharbefore(end) == EOL)
  428.             zdecr(&end);
  429.         while (end > pos && zcharbefore(end) == ' ')
  430.             zdecr(&end);
  431.         (void) tedrawtext(tp, tp->left, tp->top + i*tp->vspace,
  432.             pos, end);
  433.     }
  434.     if (tp->hilite)
  435.         teinvertfocus(tp);
  436.     
  437.     /* Reset the clip rectangle */
  438.     wnoclip();
  439. }
  440.  
  441. /* Move the gap to a new position */
  442.  
  443. temovegapto(tp, newgap)
  444.     register TEXTEDIT *tp;
  445.     bufpos newgap;
  446. {
  447.     zcheck();
  448.     zassert(0<=newgap && newgap+tp->gaplen<=tp->buflen);
  449.     
  450.     if (newgap < tp->gap)
  451.         teshift(tp, tp->gaplen, newgap, tp->gap);
  452.     else if (newgap > tp->gap)
  453.         teshift(tp, -tp->gaplen, zgapend, newgap+tp->gaplen);
  454.     tp->gap= newgap;
  455.     
  456.     zcheck();
  457. }
  458.  
  459. /* Extend the gap */
  460.  
  461. tegrowgapby(tp, add)
  462.     register TEXTEDIT *tp;
  463.     int add;
  464. {
  465.     zcheck();
  466.     zassert(add>=0);
  467.     
  468.     tp->buf= zrealloc(tp->buf, tp->buflen + add);
  469.     teshift(tp, add, zgapend, tp->buflen);
  470.     tp->gaplen += add;
  471.     if (tp->start[tp->nlines-1] == tp->buflen)
  472.         tp->start[tp->nlines-1]= tp->buflen+add;
  473.     tp->start[tp->nlines]= (tp->buflen += add);
  474.     
  475.     zcheck();
  476. }
  477.  
  478. /* Shift buf[first..last-1] n bytes (positive right, negative left) */
  479.  
  480. static
  481. teshift(tp, n, first, last)
  482.     register TEXTEDIT *tp;
  483.     int n;
  484.     bufpos first, last;
  485. {
  486.     teoffset(tp, n, first, last);
  487.     if (n < 0)
  488.         temovedown(tp, last-first, tp->buf+first, tp->buf+first+n);
  489.     else if (n > 0)
  490.         temoveup(tp, last-first, tp->buf+first, tp->buf+first+n);
  491. }
  492.  
  493. static
  494. teoffset(tp, n, first, last)
  495.     register TEXTEDIT *tp;
  496.     int n;
  497.     int first, last;
  498. {
  499.     int i;
  500.     
  501.     zassert(0<=first&&first<=last&&last<=tp->buflen);
  502.     
  503.     i= 0;
  504.     while (tp->start[i] < first)
  505.         ++i;
  506.     while (tp->start[i] < last) {
  507.         tp->start[i] += n;
  508.         ++i;
  509.     }
  510. }
  511.  
  512. /*ARGSUSED*/
  513. static
  514. temoveup(tp, n, from, to)
  515.     TEXTEDIT *tp;
  516.     int n;
  517.     char *from, *to;
  518. {
  519.     zassert(from <= to);
  520.     
  521.     from += n, to += n;
  522.     while (--n >= 0)
  523.         *--to = *--from;
  524. }
  525.  
  526. /*ARGSUSED*/
  527. static
  528. temovedown(tp, n, from, to)
  529.     TEXTEDIT *tp;
  530.     int n;
  531.     char *from, *to;
  532. {
  533.     zassert(from >= to);
  534.     
  535.     while (--n >= 0)
  536.         *to++ = *from++;
  537. }
  538.  
  539. /* Make all start entries pointing into the gap point to beyond it
  540.    TO DO: replace by a routine to delete the focus??? */
  541.  
  542. teemptygap(tp)
  543.     register TEXTEDIT *tp;
  544. {
  545.     lineno i;
  546.     
  547.     for (i= 0; tp->start[i] < tp->gap; ++i)
  548.         ;
  549.     for (; tp->start[i] < zgapend; ++i)
  550.         tp->start[i]= zgapend;
  551. }
  552.  
  553. /* Interface for wshow that clips to the viewing rectangle */
  554.  
  555. static void
  556. teshow(tp, left, top, right, bottom)
  557.     TEXTEDIT *tp;
  558.     int left, top, right, bottom;
  559. {
  560.     if (tp->viewing) {
  561.         CLIPMIN(left, tp->vleft);
  562.         CLIPMIN(top, tp->vtop);
  563.         CLIPMAX(right, tp->vright);
  564.         CLIPMAX(bottom, tp->vbottom);
  565.     }
  566.     wshow(tp->win, left, top, right, bottom);
  567. }
  568.  
  569. /* Set the caret at the new focus position,
  570.    or display the focus highlighting, if applicable.
  571.    Also call wshow() of the focus.
  572.    As a side effect, the optimization data is invalidated */
  573.  
  574. tesetcaret(tp)
  575.     register TEXTEDIT *tp;
  576. {
  577.     coord h, v, hlast, vlast;
  578.     
  579.     tp->opt_valid = FALSE;
  580.     if (!tp->active)
  581.         return;
  582.     
  583.     tewhichpoint(tp, tp->foc, &h, &v);
  584.     
  585.     if (tp->foclen == 0) {
  586.         if (!tp->viewing ||
  587.             tp->vleft <= h && h <= tp->vright &&
  588.             tp->vtop <= v && v + tp->vspace <= tp->vbottom) {
  589.             wsetcaret(tp->win, h, v);
  590.         }
  591.         else {
  592.             wnocaret(tp->win);
  593.         }
  594.         hlast= h;
  595.         vlast= v;
  596.     }
  597.     else {
  598.         tewhichpoint(tp, zfocend, &hlast, &vlast);
  599.         wnocaret(tp->win);
  600.         teshowfocus(tp);
  601.     }
  602.     teshow(tp, h, v, hlast, vlast + tp->vspace);
  603. }
  604.  
  605. /* Coordinate transformations.
  606.    The following coordinate systems exist;
  607.    a position in the text can be expressed in any of these:
  608.    
  609.        A) offset in buffer with gap removed (used for focus)
  610.     B) offset in buffer (used for start[] array)
  611.     C) (line number, offset in line taking gap into account)
  612.     D) (h, v) on screen
  613.    
  614.    Conversions exist between successive pairs:
  615.    
  616.        A -> B: pos= zaddgap(foc)
  617.     B -> A: foc= zsubgap(pos)
  618.     
  619.     B -> C: line= zwhichline(pos, prev); offset= pos-start[line]
  620.     C -> B: pos= offset + start[line]
  621.     
  622.     C -> D: v= i*vspace; h= ztextwidth(start[i], start[i]+offset)
  623.     D -> C: i= v/wlh; offset= ztextround(i, h) - start[i]
  624. */
  625.  
  626. /* Find (h, v) corresponding to focus position */
  627.  
  628. tewhichpoint(tp, f, h_ret, v_ret)
  629.     TEXTEDIT *tp;
  630.     focpos f;
  631.     coord *h_ret, *v_ret;
  632. {
  633.     bufpos pos= zaddgap(f);
  634.     lineno i= tewhichline(tp, pos, f == tp->foc && tp->focprev);
  635.     hcoord h= tetextwidth(tp, tp->start[i], pos);
  636.     
  637.     *h_ret= h + tp->left;
  638.     *v_ret= i*tp->vspace + tp->top;
  639. }
  640.  
  641. /* To which line does the given buffer position belong? */
  642.  
  643. lineno
  644. tewhichline(tp, pos, prev)
  645.     register TEXTEDIT *tp;
  646.     bufpos pos;
  647.     bool prev; /* Cf. focprev */
  648. {
  649.     lineno i;
  650.     
  651.     for (i= 0; pos > tp->start[i+1]; ++i)
  652.         ;
  653.     if (pos == tp->start[i+1] && i+1 < tp->nlines) {
  654.         ++i;
  655.         if (prev && zcharbefore(tp->start[i]) != EOL)
  656.             --i;
  657.     }
  658.     
  659.     return i;
  660. }
  661.  
  662. /* Convert point in window to buffer position,
  663.    possibly taking double-clicking into account.
  664.    If required, the line number is also returned. */
  665.  
  666. bufpos
  667. tewhereis(tp, h, v, line_return)
  668.     register TEXTEDIT *tp;
  669.     coord h, v;
  670.     int *line_return;
  671. {
  672.     bufpos pos;
  673.     lineno i;
  674.     
  675.     i= (v - tp->top)/tp->vspace;
  676.     if (i >= tp->nlines) {
  677.         i= tp->nlines;
  678.         pos= tp->buflen;
  679.     }
  680.     else if (i < 0) {
  681.         i= 0;
  682.         pos= 0;
  683.     }
  684.     else
  685.         pos= tetextround(tp, i, h);
  686.     if (line_return != NULL)
  687.         *line_return= i;
  688.     return pos;
  689. }
  690.  
  691. /* Find the buffer position nearest to the given h coordinate,
  692.    in the given line */
  693.  
  694. bufpos
  695. tetextround(tp, i, h)
  696.     register TEXTEDIT *tp;
  697.     lineno i;
  698.     hcoord h;
  699. {
  700.     bufpos pos;
  701.     bufpos end= tp->start[i+1];
  702.     
  703.     h -= tp->left;
  704.     if (end > tp->start[i] && zcharbefore(end) == EOL)
  705.         zdecr(&end);
  706.     pos= tetextbreak(tp, tp->start[i], end, h);
  707.     
  708.     if (pos < end) {
  709.         if (h - tetextwidth(tp, tp->start[i], pos) >=
  710.             tetextwidth(tp, tp->start[i], znext(pos)) - h)
  711.             zincr(&pos);
  712.     }
  713.     
  714.     return pos;
  715. }
  716.